#include "Cellule.hpp"

#include "Camera.hpp"
#include "Environnement.hpp"

#include "Materiaux/Melange.hpp"

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

Cellule::Cellule( Camera * cam, const unsigned& minX, const unsigned& maxX,
		  const unsigned& minY, const unsigned& maxY )
  #ifdef MULTI_THREAD
  #else // MULTI_THREAD
  : PtrCamera( cam ), Scene( 0 ),
    MinX( minX ), MaxX( maxX ),  MinY( minY ), MaxY( maxY ), RenduPrimaire( true )
  #endif // MULTI_THREAD
{}

Cellule::~Cellule()
{}

void Cellule::genereCellule()
{
  Scene = PtrCamera -> Env;

  if ( MinX == -1u )
    MinX = 0u;
  if ( MaxX == -1u )
    MaxX = PtrCamera -> SuperFilm -> largeur();
  if ( MinY == -1u )
    MinY = 0u;
  if ( MaxY == -1u )
    MaxY = PtrCamera -> SuperFilm -> hauteur();

  FocaleEff = PtrCamera -> Distance *
    double( PtrCamera -> FacteurOversampling );
  DirCopie = PtrCamera -> Direction;
  HautCopie = PtrCamera -> Hauteur;
  LargCopie = PtrCamera -> Largeur;
  PosCopie = PtrCamera -> Position;

  if ( RenduPrimaire )
    effectueRenduPrimaire();
  else
    effectueRenduSecondaire();
}

const bool& Cellule::renduPrimaire() const
{
  return RenduPrimaire;
}

void Cellule::setRenduPrimaire( const bool& prim )
{
  RenduPrimaire = prim;
}

const unsigned& Cellule::minX() const
{
  return MinX;
}

unsigned& Cellule::rMinX()
{
  return MinX;
}

void Cellule::setMinX( const unsigned& min )
{
  MinX = min;
}

const unsigned& Cellule::maxX() const
{
  return MaxX;
}

unsigned& Cellule::rMaxX()
{
  return MaxX;
}

void Cellule::setMaxX( const unsigned& max )
{
  MaxX = max;
}

const unsigned& Cellule::minY() const
{
  return MinY;
}

unsigned& Cellule::rMinY()
{
  return MinY;
}

void Cellule::setMinY( const unsigned& min )
{
  MinY = min;
}

const unsigned& Cellule::maxY() const
{
  return MaxY;
}

unsigned& Cellule::rMaxY()
{
  return MaxY;
}

void Cellule::setMaxY( const unsigned& max )
{
  MaxY = max;
}


void Cellule::effectueRenduPrimaire()
{
    int x, y;

    VecteurU dir;
    Rayon ray;

    for ( unsigned i( MinY ), j; i < MaxY; i++ )
    {
        for ( j = MinX; j < MaxX; j++ )
        {
            x = PtrCamera -> SuperFilm -> largeur() / 2 - j;
            y = PtrCamera -> SuperFilm -> hauteur() / 2 - i;

            dir = VecteurU( FocaleEff * DirCopie +
                            double( x ) * LargCopie +
                            double( y ) * HautCopie );

            ray = Rayon( PosCopie, dir );

#ifdef MULTI_THREAD
            PtrCamera -> verouilleAccesImage();
#endif // MULTI_TREAD
            PtrCamera -> SuperFilm -> setPixel( i, j, Scene -> lanceRayon( ray, PtrCamera -> Precision, 1.0 ) );
            PtrCamera -> incrementeProgression();
#ifdef MULTI_THREAD
            PtrCamera -> libereAccesImage();
#endif // MULTI_TREAD
        }
    }

#ifdef MULTI_TREAD
    PtrCamera -> libereRenduImage();
#endif // MULTI_TREAD
}

void Cellule::effectueRenduSecondaire()
{
    Couleur col;
    vector< VecteurU > directions;
    vector< Rayon > rayons;
    vector< Couleur > couleurs;

    vector< double > poids;

    if ( PtrCamera -> Anti  == simple )
        poids = list_of( .25 )( .25 )( .25 )( .25 );
    else if ( PtrCamera -> Anti == bourrin )
        poids = list_of( .2 )( .2 )( .2 )( .2 )( .2 );
    else if ( PtrCamera -> Anti == ultra_bourrin )
        poids = list_of( .1 )( .075 )( .075 )( .075 )( .075 )
                ( .075 )( .075 )( .075 )( .075 )( .075 )( .075 )( .075 )( .075 );
    else
        return;

    double x, y;

    unsigned i, j, k, largeur( PtrCamera -> Film -> largeur() ),
            hauteur( PtrCamera -> Film -> hauteur() ), nombre( poids.size() );

    for ( i = MinY; i < MaxY; i++ )
    {
        for ( j = MinX; j < MaxX; j++ )
        {
#ifdef MULTI_THREAD
            PtrCamera -> verouilleAccesImage();
#endif // MULTI_THREAD
            PtrCamera -> incrementeProgression();
#ifdef MULTI_THREAD
            PtrCamera -> libereAccesImage();
#endif // MULTI_THREAD

            if ( ! PtrCamera -> Contour[ i ][ j ] )
            {
                continue;
            }

            x = static_cast< double >( largeur / 2 ) -
                    static_cast< double >( j );
            y = static_cast< double >( hauteur / 2 ) -
                    static_cast< double >( i );

            directions.clear();
            rayons.clear();
            couleurs.clear();

            PtrCamera -> determinerDirections( directions, x, y );

            for ( k = 0; k < nombre; k++ )
            {
                rayons.push_back( Rayon( PosCopie, directions[ k ] ) );
                couleurs.push_back( Scene -> lanceRayon( rayons[ k ],
                                                         PtrCamera -> precision(), 1.0 ) );
            }

            col = faireMelangeCouleurs( couleurs, poids );
#ifdef MULTI_THREAD
            PtrCamera -> verouilleAccesImage();
#endif // MULTI_TREAD
            PtrCamera -> Compteur++;
            PtrCamera -> Film -> setPixel( i, j, col );
#ifdef MULTI_THREAD
            PtrCamera -> libereAccesImage();
#endif // MULTI_TREAD
        }
    }

}
